#include "socket.h"

char SECRETKEY[32]	= "88888888";	//注册VIP密钥
/*
功能：初始化服务器网络配置
形参：无
返回值：服务器套接字
 */
int InitNet()
{
	int sockfd, ret;
	struct sockaddr_in server_addr;    //保存服务器本身信息

	//创建socket
	sockfd = socket(PF_INET, SOCK_STREAM, 0);  //1、地址族 2、流式套接字 3、具体的协议类型
	if (-1 == sockfd)
	{
		perror("socket");
		exit(1);
	}

	int opt = 1;
	setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));	//重复绑定

	//绑定
	memset(&server_addr, 0, sizeof(server_addr));
	server_addr.sin_family = PF_INET;    //地址族
	server_addr.sin_port = 8000;         //服务器接下来要监听的端口号
	server_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if (-1 == ret)
	{
		perror("bind");
		exit(1);
	}

	//监听
	ret = listen(sockfd, 10);  //10表示监听队列长度
	if (-1 == ret)
	{
		perror("listen");
		exit(1);
	}
	
	return sockfd;
}

/*
功能：获得系统时间：年、月、日、时、分、秒
形参：字符串地址(输出参数)
返回值：无
头文件：<time.h>
 */
void GetTime(char *str_time)
{
	int arr_time[6];
	// arr_time = (int *)malloc(sizeof(int)*6);
	time_t t;
    struct tm * lt;
    time (&t);				//获取Unix时间戳。
    lt = localtime (&t);	//转为时间结构
    // printf ( "%d/%d/%d %d:%d:%d\n",lt->tm_year+1900, lt->tm_mon, lt->tm_mday, 
    // lt->tm_hour, lt->tm_min, lt->tm_sec);//输出结果
    arr_time[0] = lt->tm_year + 1900;
    arr_time[1]	= lt->tm_mon;
    arr_time[2]	= lt->tm_mday;
    arr_time[3]	= lt->tm_hour;
    arr_time[4]	= lt->tm_min;
    arr_time[5]	= lt->tm_sec;
    sprintf(str_time, "%d/%d/%d %d:%d:%d ",arr_time[0], arr_time[1], arr_time[2], arr_time[3], arr_time[4], arr_time[5]);
}

/*
功能：注册处理函数
形参：1、聊天信息结构体地址 2、新建在线信息结点地址
返回值：无
 */
void register_handler(Chat *c, Online *Node_tmp)
{
	int ret;
	//初始化结点信息
	strcpy(Node_tmp->id, c->id);
	Node_tmp->forbid_flag = 0;
	Node_tmp->admin_flag = 0;

	ret = if_user_exist(c->id);
	if (TRUE == ret)    //表示存在 回复客户端，账号存在
	{
		c->result = RES_USEREXIST;
		ret = send(Node_tmp->fd, c, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("send");
			exit(1);
		}
	}
	else               //表示不存在 写入数据库，同时回复注册成功
	{	
		ret = write_database_userinfo(c);
		if (FAILURE == ret)
		{
			printf("write database failure!\n");
		}
	
		c->result = RES_SUCCESS;
		ret = send(Node_tmp->fd, c, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("send");
			exit(1);
		}
	}
}

/*
功能：客户端登录处理函数
形参：1、聊天信息结构体地址 2、新建在线信息结点地址 3、链表头结点地址
返回值：无
 */
void login_handler(Chat *c, Online *Node_tmp, Online *head)
{
	int ret;
	Chat c_tmp;
	Online *Node, *Node_ID;

	//初始化结点信息
	strcpy(Node_tmp->id, c->id);
	Node_tmp->forbid_flag = 0;
	Node_tmp->admin_flag = 0;

	ret = if_user_exist(c->id);
	if (FALSE == ret)	//账户不存在
	{
		c_tmp.result = RES_USERNOTEXIST;	//
		ret = send(Node_tmp->fd, &c_tmp, sizeof(c_tmp), 0);
		if (-1 == ret)
		{
			perror("login_send");
			exit(1);
		}
	}
	else if (TRUE == ret)
	{
		Node_ID = GetNodeFromID(head,c->id);
		// printf("Node_ID = %s\n", Node_ID->id);
		if (Node_ID != NULL && Node_ID->status == 1)
		{
			c_tmp.result = RES_USERONLINE;	

			ret = send(Node_tmp->fd, &c_tmp, sizeof(Chat), 0);
			if (-1 == ret)
			{
				perror("login_handler_send1");
				exit(1);
			}	
			// printf("以在线！\n");		
			return;
		}	

		ret = if_passwd_exist(c->id, c->passwd);	//判断数据库密码是否匹配
		if (TRUE == ret)	//密码正确
		{	
			printf("用户：%s 登陆成功！\n", c->id);

			if(if_user_admin(c->id) == TRUE)		//判断数据库是否为管理员
			{
				c_tmp.admin_flag = 1;
			}
			else
			{
				c_tmp.admin_flag = 0;	
			}

			Node = (Online*)malloc(sizeof(Online));
			memcpy(Node, Node_tmp, sizeof(Online));
			Node->status = 1;						//状态在线
			Node->admin_flag = c_tmp.admin_flag ;	//管理员标志位
			Node->forbid_flag = 0;					//禁言标志位

			ret = InsertLink(head, 1, Node);		//登陆成功将用户节点信息插入链表
			if (FAILURE == ret)
			{
				printf("插入链表失败\n");
			}

			c_tmp.result = RES_SUCCESS;
		
			ret = send(Node_tmp->fd, &c_tmp, sizeof(Chat), 0);
			if (-1 == ret)
			{
				perror("login_handler_send2");
				exit(1);
			}		
		}
		else
		{
			c_tmp.result = RES_PASSWDERROR;

			ret = send(Node_tmp->fd, &c_tmp, sizeof(Chat), 0);
			if (-1 == ret)
			{
				perror("login_handler_send3");
				exit(1);
			}
		}
	}
}

/*
功能：获得在线用户ID
形参：1、接收到的Chat结构体地址 2、文件描述符套接字 3、在线用户信息链表头结点地址
返回值：无
 */
void onlineuser_handler(Chat *c_recv, int fd, Online *head)
{
	// printf("给%s在线用户信息发送完毕!\n", c_recv->id);
	Chat c;
	int ret;

	Online *l = head;
	//遍历链表
	// ret = TraverseLink(head);
	// if (FAILURE == ret)
	// {
	// 	printf("TraverseLink Failure!\n");
	// }
	while(l->next != NULL)
	{
		l = l->next;
		c.result = RES_WAITINGONLINE;
		strcpy(c.id_to, l->id);
		// printf("ID = %s\n", c.id_to);
		ret = send(fd, &c, sizeof(c), 0);
		if (-1 == ret)
		{
			perror("onlineuser_handler_send1");
			exit(1);
		}
	}

	c.result = RES_SUCCESS;

	ret = send(fd, &c, sizeof(c), 0);
	if (-1 == ret)
	{
		perror("onlineuser_handler_send2");
		exit(1);
	}

	// printf("给%s在线用户信息发送完毕!\n", c_recv->id);
}

/*
功能：私信处理函数
形参：1、文件描述符 2、接收到的消息结构体地址 3、链表头结点
返回值：无
 */
void private_handler(int fd, Chat *c, Online *head)
{
	if (NULL == head || NULL == c)
	{
		return;
	}
	// printf("目标用户：%s\n", c->id_to);
	Online *Node; //保存目标用户信息结点地址
	int ret;
	Chat c_send;
	char time[32] = {0};

	memcpy(&c_send, c, sizeof(Chat));
	//消息存入数据库		

	Node = GetNodeFromID(head,c->id_to);
	if (Node == NULL)
	{
		c_send.result = RES_USEROFFLINE;

		ret = send(fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("private_handler_send_from");
			exit(1);
		}
		// printf("发回不在线！\n");
		return;
	}

	if (1 == Node->status)
	{
		c_send.result = RES_MESSAGE;
	
		//给目标目标用户发送 
		ret = send(Node->fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("private_handler_send_to");
			exit(1);
		}		

		//存储消息
		GetTime(time);		
		write_database_ChatInfo(&c_send, time);

		// printf("转发成功！\n");
		c_send.result = RES_SUCCESS;
		ret = send(fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("private_handler_send_from");
			exit(1);
		}
	}
	else
	{
		c_send.result = RES_USEROFFLINE;

		ret = send(fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("private_handler_send_from");
			exit(1);
		}
		// printf("发回不在线！\n");ssss
	}

}
/*
功能：群发处理函数
形参：1、文件描述符 2、接收到的消息结构体地址 3、链表头结点
返回值：无
 */
void group_handler(int fd, Chat *c, Online *head)
{
	if (NULL == head || NULL == c)
	{
		return;
	}
	// printf("目标用户：%s\n", c->id_to);
	Online *Node; //保存目标用户信息结点地址
	int ret;
	Chat c_send, c_tmp;
	char time[32] = {0};

	// printf("id= %s\n", c->id);
	memcpy(&c_send, c, sizeof(Chat));
	memcpy(&c_tmp, c, sizeof(Chat));
	//消息存入数据库		
	strcpy(c_tmp.id_to, "ALLUSER");
	GetTime(time);		
	write_database_ChatInfo(&c_tmp, time);

	Node = GetNodeFromID(head,c->id_to);
	if (Node == NULL)
	{
		c_send.result = RES_USEROFFLINE;

		ret = send(fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("private_handler_send_from");
			exit(1);
		}
		// printf("发回不在线！\n");
		return;
	}

	if (1 == Node->status)
	{
		c_send.result = RES_MESSAGE;
		//给目标目标用户发送 
		ret = send(Node->fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("private_handler_send_to");
			exit(1);
		}	
		// printf("转发成功！\n");		

		c_send.result = RES_SUCCESS;
		ret = send(fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("private_handler_send_from");
			exit(1);
		}

	}
	else
	{
		c_send.result = RES_USEROFFLINE;

		ret = send(fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("private_handler_send_from");
			exit(1);
		}
		// printf("发回不在线！\n");
	}

}
/*
功能：注册VIP处理函数
形参：1、文件描述符 2、接收到的消息结构体地址 3、链表头结点
返回值：无
 */
void register_vip(int fd, Chat *c, Online *head)
{
	Chat c_send;
	int ret;
	Online *Node;
	memcpy(&c_send, c, sizeof(Chat));

	Node = GetNodeFromID(head,c->id);
	// printf("Node = %p\n", Node);

	if (Node->admin_flag == 1)
	{
		// printf("%s已经是VIP\n", c->id);
		c_send.result = RES_VIPEXIST;
		ret = send(fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("register_vip_send3");
			exit(1);
		}
		return;
	}

	if (strcmp(SECRETKEY, c->text) == 0)
	{
		// printf("注册VIP成功！\n");

		Node->admin_flag = 1;

		ret = write_database_admin_flag(Node);	//写入数据库
		if (FAILURE == ret)
		{
			printf("插入数据库失败！\n");
		}

		c_send.result = RES_SUCCESS;
		ret = send(fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("register_vip_send1");
			exit(1);
		}
	}
	else
	{
		c_send.result = RES_NOTADMIN;
		ret = send(fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("register_vip_send2");
			exit(1);
		}
	}

}

/*
功能：禁言处理函数
形参：1、文件描述符 2、接收到的消息结构体地址 3、链表头结点
返回值：无
 */
void forbid_user_handler(int fd, Chat *c, Online *head)
{
	if (NULL == head || NULL == c)
	{
		return;
	}
	int ret;
	Chat c_send;
	memcpy(&c_send, c, sizeof(Chat));

	Online *Node;
	Node = GetNodeFromID(head,c->id_to);

	ret = if_user_exist(c->id_to);
	if (FALSE == ret)	//账户不存在
	{
		c_send.result = RES_USERNOTEXIST;	
		ret = send(fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("forbid_user_handler_send1");
			exit(1);
		}
	}
	else
	{
		if (NULL == Node || Node->status == 0)
		{
			c_send.result = RES_USEROFFLINE;
			ret = send(fd, &c_send, sizeof(Chat), 0);
			if (-1 == ret)
			{
				perror("forbid_user_handler_send2");
				exit(1);
			}
		}
		else if (Node->admin_flag == 1)
		{
			c_send.result = RES_VIPEXIST;	
			ret = send(fd, &c_send, sizeof(Chat), 0);
			if (-1 == ret)
			{
				perror("forbid_user_handler_send3");
				exit(1);
			}		
		}
		else
		{
			c_send.result = RES_FORBIDSUCCESS;
			sprintf(c_send.text, "您已被%s禁言！", c->id);
			Node->forbid_flag = 1;
			ret = send(Node->fd, &c_send, sizeof(Chat), 0);
			if (-1 == ret)
			{
				perror("forbid_user_handler_send4");
				exit(1);
			}

			c_send.result = RES_SUCCESS;
			ret = send(fd, &c_send, sizeof(Chat), 0);
			if (-1 == ret)
			{
				perror("forbid_user_handler_send5");
				exit(1);
			}
		}
	}
}
/*
功能：踢出用户处理函数
形参：1、文件描述符 2、接收到的消息结构体地址 3、链表头结点
返回值：无
 */
void kick_out_user_handler(int fd, Chat *c, Online *head)
{
	if (NULL == head || NULL == c)
	{
		return;
	}
	int ret;
	Chat c_send;
	memcpy(&c_send, c, sizeof(Chat));

	Online *Node;
	Node = GetNodeFromID(head,c->id_to);

	ret = if_user_exist(c->id_to);
	if (FALSE == ret)	//账户不存在
	{
		c_send.result = RES_USERNOTEXIST;	
		ret = send(fd, &c_send, sizeof(Chat), 0);
		if (-1 == ret)
		{
			perror("kick_out_user_handler_send1");
			exit(1);
		}
	}
	else
	{
		if (NULL == Node || Node->status == 0)
		{
			c_send.result = RES_USEROFFLINE;
			ret = send(fd, &c_send, sizeof(Chat), 0);
			if (-1 == ret)
			{
				perror("kick_out_user_handler_send2");
				exit(1);
			}
		}
		else if (Node->admin_flag == 1)
		{
			c_send.result = RES_VIPEXIST;	
			ret = send(fd, &c_send, sizeof(Chat), 0);
			if (-1 == ret)
			{
				perror("kick_out_user_handler_send3");
				exit(1);
			}		
		}
		else
		{
			c_send.result = RES_KICKOUT;
			sprintf(c_send.text, "您已被%s踢下线！", c->id);

			ret = DeleteNodeFromID(head, c->id_to);
			if (FAILURE == ret)
			{
				printf("删除踢出结点失败！\n");
			}

			ret = send(Node->fd, &c_send, sizeof(Chat), 0);
			if (-1 == ret)
			{
				perror("kick_out_user_handler_send4");
				exit(1);
			}

			c_send.result = RES_SUCCESS;
			ret = send(fd, &c_send, sizeof(Chat), 0);
			if (-1 == ret)
			{
				perror("kick_out_user_handler_send5");
				exit(1);
			}
		}
	}

}

void file_transfer(int fd, Chat *c, Online *head)
{
	if (NULL == head || NULL == c)
	{
		return;
	}
	int ret;
	Chat c_send;
	memcpy(&c_send, c, sizeof(Chat));
	Online *Node;
	Node = GetNodeFromID(head,c->id_to);


	ret = send(Node->fd, &c_send, sizeof(Chat), 0);
	if (-1 == ret)
	{
		perror("file_transfer_send1");
		exit(1);
	}

	// c_send.result = RES_FILESEND;
	// ret = send(fd, &c_send, sizeof(Chat), 0);
	// if (-1 == ret)
	// {
	// 	perror("file_transfer_end2");
	// 	exit(1);
	// }
}
/*
功能：线程接收消息命令处理函数
形参：(void *)客户端文件描述符
返回值：无
 */
void *RecvHandler(void *arg)
{
	struct head_fd_struct *head_fd = (struct head_fd_struct *)arg;

	//通过结构体传多个变量
	Online *head = head_fd->head_s;
	int fd = head_fd->fd_s;

	int ret;
	Chat c;

	Online *Node_tmp = (Online *)malloc(sizeof(Online));


	pthread_detach(pthread_self());
	while(1)
	{
		ret = recv(fd, &c, sizeof(c), 0);	//从TCP--accept的fd 接收消息 存入Chat c结构体
		if(-1 == ret)
		{
			perror("recv");
			exit(1);
		}

		Node_tmp->fd = fd;
		strcpy(Node_tmp->id, c.id);	//将用户ID存入结构体

		// printf("CMD = %d\n", c.cmd);	
		// printf("ID = %s\n", .id);
		switch(c.cmd)
		{	
			case CMD_REGISTER:
				register_handler(&c, Node_tmp);		//注册
				break;
			case CMD_LOGIN:
				login_handler(&c, Node_tmp, head);	//登录
				break;
			case CMD_ONLINEUSER:
				onlineuser_handler(&c, fd, head);	//查看在线用户
				break;
			case CMD_PRIVATE:
				private_handler(fd, &c, head);		//私聊
				break;
			case CMD_GROUP:
				group_handler(fd, &c, head);		//群发
				break;
			case CMD_FORBID:
				forbid_user_handler(fd, &c, head);	//禁言
				break;
			case CMD_KICKOUT:
				kick_out_user_handler(fd, &c, head);//踢人
				break;
			case CMD_OFFLINE:
				userexit(fd, &c, head);				//下线
				break;
			case CMD_REGISTERVIP:
				register_vip(fd, &c, head);			//注册会员
				break;
			case CMD_FILE:
				file_transfer(fd, &c, head);		//传输文件
				break;
			case CMD_GETCHATINFO:
				get_database_chatinfo(fd, c.id);	//聊天信息
				break;
			case CMD_EXIT:
				main_exit(fd, &c, head, Node_tmp);	//客户端退出
				break;
			default :
				break;
		}		
		memset(&c, 0, sizeof(c));	//清空c中内容，用于下次接收
	}
	close(fd);
	return NULL;
}

/*
功能：客户端退出处理函数
形参：1、文件描述符 2、接收到的消息结构体地址 3、链表头结点 4、需要释放的临时结点
返回值：无
 */
void main_exit(int fd, Chat *c, Online *head, Online *Node_tmp)
{
	Chat c_send;
	int ret;

	memcpy(&c_send, c, sizeof(Chat));
	c_send.result = RES_EXIT;

	ret = send(fd, &c_send, sizeof(Chat), 0);

	if (-1 == ret)
	{
		perror("main_exit_send");
		exit(1);
	}

	printf("客户端%d已退出...\n", fd);
	free(Node_tmp);
	sleep(1);
	close(fd);
	fd = 0;
	pthread_exit((void*)1);
}

void userexit(int fd, Chat *c, Online *head)
{
	Chat c_send;
	int ret;

	if (RES_ASKEXIT == c->result)
	{
		printf("用户:%s已下线！\n", c->id);

		ret = DeleteNodeFromID(head, c->id);
		if (FAILURE == ret)
		{
			printf("删除下线结点失败！\n");
		}

		c_send.result = RES_USEREXIT;
		

		ret = send(fd, &c_send, sizeof(c_send), 0);
		if (-1 == ret)
		{
			perror("userexit_send");
			exit(1);
		}
	}
}
/*
功能：等待客户端连接，并建立线程
形参：1、服务器套接字 2、Online头结点地址
返回值：无
 */
void MainHandler(int sockfd, Online *head)
{
	int fd = 0;
	int  ret;

	struct head_fd_struct head_fd;
	struct sockaddr_in client_addr;

	int length = sizeof(client_addr);

	while (1)
	{	
		printf("等待客户端连接...\n");
		fd= accept(sockfd, (struct sockaddr *)&client_addr, (socklen_t *)&length);
		if (-1 == fd)
		{
			perror("accept");
		}

		printf("客户端%d已连接...\n", fd);
			
		head_fd.head_s = head;
		head_fd.fd_s = fd;
		//传两个以上参数，通过结构体
		
		pthread_t tid;
		ret = pthread_create(&tid, NULL, RecvHandler, (void*)&head_fd);		//有客户端连接，创建线程
		if (-1 == ret)
		{
			perror("pthread_create");
			exit(1);
		}
	}
}

